package com.algorithm.qap;

import java.util.ArrayList;
import java.util.Arrays;
import com.algorithm.basic.BasicMoead;
import com.problem.qap.QAPProblem;
import com.util.random.RandomGenerator;
import com.util.solution.impl.QAPSolution;

public class MOMAD extends BasicMoead{
	private QAPProblem problem;
	private int run_num;
	private int MAX = 10;
	public MOMAD(QAPProblem problem, int run_num){
		super(problem.obj_num, -1);
		this.problem = problem;
		this.run_num = run_num;
		weights = new double[problem.sub_num*problem.obj_num];
		nicheNeighbors = new int[problem.sub_num][1];
		initialWeights(problem.sub_num, 1);
		this.bind();
	}
	public void bind(){
		for(int i=0;i<problem.sub_num;i++){
			int [] values = problem.solutionArray[i].object_val;
			problem.solutionArray[i].fitness = getWsValue(i, values);
		}
	}
	public void execute(int iteration){
		for(int i=1;i<=iteration;i++){
			System.out.println("iteration of : "+i);
			this.localSearch();
			this.paretoLocalSearch();
			this.problem.kdSet.clearAndReset();
		}
		String fileName = "files/results/qap/momad/"+problem.fac_num+"-"+problem.obj_num+"/"+this.problem.corr+"/"+problem.fac_num+"_"+problem.obj_num+"_momad_"+run_num+".txt";
		this.problem.kdSet.saveObjectiveResults(fileName);
	}
	public void paretoLocalSearch(){
		if(this.problem.kdStartingSet.getPopSize()==0) return;
		ArrayList<QAPSolution> solutions = new ArrayList<>();
		for(QAPSolution s : this.problem.kdStartingSet.getSolutions()){
			try {
				solutions.add(s.clone());
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
		int t_count = 0;
		while(t_count++<MAX){
			this.problem.kdStartingSet.clear();
			for(QAPSolution s : solutions){
				this.generateNeighborhood(s);
			}
			solutions.clear();
			for(QAPSolution s : this.problem.kdStartingSet.getSolutions()){
				try {
					solutions.add(s.clone());
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
			}
			this.problem.kdStartingSet.clear();
		}
	}
	/**
	 * generate the neighborhood
	 * @param s
	 */
	public void generateNeighborhood(QAPSolution s){
		int [] seq = s.getLocations().clone();
		for(int i=0;i<problem.fac_num;i++){
			for(int j=i+1;j<problem.fac_num;j++){
				if(i==j) continue;
				int [] changedVal = new int[problem.obj_num];
				for(int k=0;k<problem.obj_num;k++) changedVal[k] = 0;
				for(int k=0;k<problem.obj_num;k++){
					for(int t=0;t<this.problem.fac_num;t++){
						changedVal[k] += 2*(this.problem.distMatrix[i*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+seq[i]*this.problem.fac_num+seq[t]]);
						changedVal[k] += 2*(this.problem.distMatrix[j*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+seq[j]*this.problem.fac_num+seq[t]]);
					}
				}
				QAPSolution temp = new QAPSolution(this.problem.fac_num, this.problem.obj_num, -1);
				temp.setLocations(seq.clone());
				temp.locations[i] = seq[j];
				temp.locations[j] = seq[i];
				for(int k=0;k<this.problem.obj_num;k++){
					for(int t=0;t<this.problem.fac_num;t++){
						changedVal[k] -= 2*(this.problem.distMatrix[i*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+temp.locations[i]*this.problem.fac_num+temp.locations[t]]);
						changedVal[k] -= 2*(this.problem.distMatrix[j*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+temp.locations[j]*this.problem.fac_num+temp.locations[t]]);
					}
				}
				int t_count = 0;
				for(int k=0;k<this.problem.obj_num;k++){
					if(changedVal[k]<=0){
						t_count++;
					}
				}
				if(t_count==this.problem.obj_num){
					continue;
				}
				for(int k=0;k<this.problem.obj_num;k++) temp.object_val[k] = s.object_val[k] - changedVal[k];
				this.updateSubSolutions(temp);
				if(this.problem.kdSet.add(temp)){
					this.problem.kdStartingSet.add(temp);
				}
			}
		}
	}
	/**
	 * the iterator local search process
	 */
	public void localSearch(){
		for(QAPSolution s : this.problem.solutionArray){
			try {
				QAPSolution temp = s.clone();
				this.perturbation(temp);
				this.iteratorLS(temp);
				this.updateSubSolutions(temp);
				if(this.problem.kdSet.add(temp)){
					this.problem.kdStartingSet.add(temp);
				}
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * the local search process
	 * @param s
	 */
	public void iteratorLS(QAPSolution s){
		while(true){
			int [] seq = s.getLocations().clone();
			boolean updated = false;
			for(int i=0;i<problem.fac_num;i++){
				for(int j=i+1;j<problem.fac_num;j++){
					updated = false;
					if(i==j) continue;
					int [] changedVal = new int[problem.obj_num];
					for(int k=0;k<problem.obj_num;k++) changedVal[k] = 0;
					for(int k=0;k<problem.obj_num;k++){
						for(int t=0;t<this.problem.fac_num;t++){
							changedVal[k] += 2*(this.problem.distMatrix[i*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+seq[i]*this.problem.fac_num+seq[t]]);
							changedVal[k] += 2*(this.problem.distMatrix[j*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+seq[j]*this.problem.fac_num+seq[t]]);
						}
					}
					QAPSolution temp = new QAPSolution(this.problem.fac_num, this.problem.obj_num, -1);
					temp.setLocations(seq.clone());
					temp.locations[i] = seq[j];
					temp.locations[j] = seq[i];
					temp.index = s.index;
					for(int k=0;k<this.problem.obj_num;k++){
						for(int t=0;t<this.problem.fac_num;t++){
							changedVal[k] -= 2*(this.problem.distMatrix[i*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+temp.locations[i]*this.problem.fac_num+temp.locations[t]]);
							changedVal[k] -= 2*(this.problem.distMatrix[j*this.problem.fac_num+t]*this.problem.flowMatrix[k*this.problem.fac_num*this.problem.fac_num+temp.locations[j]*this.problem.fac_num+temp.locations[t]]);
						}
					}
					for(int k=0;k<this.problem.obj_num;k++) temp.object_val[k] = s.object_val[k] - changedVal[k];
					double fitness = getWsValue(s.index, temp.object_val);
					if(s.fitness<=fitness) continue;
					updated = true;
					s.setLocations(temp.locations);
					s.setObject_val(temp.object_val);
					s.fitness = fitness;
					break;
				}
				if(updated) break;
			}
			if(!updated) {
				break;
			}
		}
	}
	public void perturbation(QAPSolution s){
		int [] rand_array = RandomGenerator.permutation_array(0, this.problem.fac_num-1);
		int opt = 4;
		int [] chosed_index = Arrays.copyOf(rand_array, opt);
		Arrays.sort(chosed_index);
		int first = s.locations[chosed_index[0]];
		for(int i=0;i<opt-1;i++){
			s.locations[chosed_index[i]] = s.locations[chosed_index[i+1]];
		}
		s.locations[chosed_index[opt-1]] = first;
		this.problem.calculateObjective(s);
	}
	public boolean updateSubSolutions(QAPSolution s){
		int [] rand_array = RandomGenerator.permutation_array(0, this.problem.sub_num-1);
		for(int rd : rand_array){
			QAPSolution curSolution = problem.solutionArray[rd];
			double fitness = getWsValue(curSolution.index, s.object_val);
			if(curSolution.fitness>fitness){
				curSolution.fitness = fitness;
				curSolution.object_val = s.object_val.clone();
				curSolution.locations = s.locations.clone();
				curSolution.setSearched(false);
				return true;
			}
		}
		return false;
	}

	public static void main(String[]args){
		for(int i=0;i<20;i++){
			QAPProblem problem = new QAPProblem();
			MOMAD momad = new MOMAD(problem, i);
			momad.execute(1000);
		}
	}
}
